﻿using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security.Principal;
using System.Configuration;
using System.IO;
using System.Threading.Tasks;
using System.Linq;

namespace CloudStorageLight.Core.Tests
{
    /// <summary>
    /// BlobServiceTest の概要の説明
    /// </summary>
    [TestClass]
    public class BlobServiceBaseTest
    {
        public BlobServiceBaseTest()
        {
            //
            // TODO: コンストラクター ロジックをここに追加します
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///現在のテストの実行についての情報および機能を
        ///提供するテスト コンテキストを取得または設定します。
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region 追加のテスト属性
        //
        // テストを作成する際には、次の追加属性を使用できます:
        //
        // クラス内で最初のテストを実行する前に、ClassInitialize を使用してコードを実行してください
        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext) 
        {
            var fullTextService = new FullTextServiceBase(ConfigurationManager.AppSettings["blobService.containerName"] + "-catalog", ConfigurationManager.AppSettings["blobService.connectionString"]);
            fullTextService.Init();

            using (var svc = CreateService()){
                if (!svc.ExistsAsync("/単体テスト用/").Result)
                {
                    var testRoot = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/", "test").Result;
                    testRoot.SetAccessToken("everyone", "r");
                    testRoot.SetMetadataAsync().Wait();
                }

                if (!svc.ExistsAsync("/単体テスト用/サブディレクトリ 1/").Result)
                {
                    var dire1 = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 1/", "test").Result;
                }
                if (!svc.ExistsAsync("/単体テスト用/サブディレクトリ 2/").Result)
                {
                    var dire2 = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/", "test").Result;
                    var dire2sub = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ/", "test").Result;
                    var dire22sub = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/", "test").Result;
                    using (var st = new MemoryStream(Encoding.UTF8.GetBytes("サンプルテキスト")))
                    {
                        var file = svc.UploadFileAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/テキスト.txt", st).Result;
                        svc.UpdateFullTextAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/テキスト.txt", "テキスト.txt", "サンプルテキスト").Wait();
                    }
                }
                else
                {
                    foreach (var file in svc.ListFilesAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ/", 1000).Result)
                    {
                        if (file is BlobFile)
                        {
                            svc.DeleteFileAsync(CreateTestAdminUser(), file.Path).Wait();
                        }
                    }
                    if (svc.ExistsAsync("/単体テスト用/サブディレクトリ 2/サブサブ/").Result)
                    {
                        svc.DeleteDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ/").Wait();
                    }
                    foreach (var file in svc.ListFilesAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/", 1000).Result)
                    {
                        if (file is BlobFile)
                        {
                            svc.DeleteFileAsync(CreateTestAdminUser(), file.Path).Wait();
                        }
                    }
                    if (svc.ExistsAsync("/単体テスト用/サブディレクトリ 2/サブサブ 2/").Result)
                    {
                        svc.DeleteDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/").Wait();
                    }
                    if (svc.ExistsAsync("/単体テスト用/サブディレクトリ 2/サブサブ 2/テキスト.txt").Result)
                    {
                        svc.DeleteFileAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/テキスト.txt").Wait();
                    }
                    foreach (var file in svc.ListFilesAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/", 1000, true).Result)
                    {
                        if (file is BlobFile)
                        {
                            svc.DeleteFileAsync(CreateTestAdminUser(), file.Path).Wait();
                        }
                    }
                    svc.DeleteDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/").Wait();
                    var dire2 = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/", "test").Result;
                    var dire2sub = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ/", "test").Result;
                    var dire22sub = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/", "test").Result;
                    using (var st = new MemoryStream(Encoding.UTF8.GetBytes("サンプルテキスト")))
                    {
                        var file = svc.UploadFileAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/テキスト.txt", st).Result;                       
                        svc.UpdateFullTextAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/サブサブ 2/テキスト.txt", "テキスト.txt", "サンプルテキスト").Wait();
                    }
                }
                if (!svc.ExistsAsync("/単体テスト用/サブディレクトリ 3/").Result)
                {
                    var dire3 = svc.CreateDirectoryAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 3/", "test").Result;
                }
                else
                {
                    foreach (var file in svc.ListFilesAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 3/", 1000).Result)
                    {
                        if (file is BlobFile)
                        {
                            svc.DeleteFileAsync(CreateTestAdminUser(), file.Path).Wait();
                        }
                    }
                }

                using (var st = new MemoryStream(Encoding.UTF8.GetBytes("サンプルテキスト")))
                {
                    var file = svc.UploadFileAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/テキスト.txt", st).Result;
                }
                for (int i= 0; i < 5; i++)
                {
                    try
                    {
                        svc.UpdateFullTextAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/テキスト.txt", "テキスト.txt", "サンプルテキスト").Wait();
                        svc.ExecuteUpdateFullText().Wait();
                        break;
                    }
                    catch (Exception)
                    {
                        if (i == 4) throw;
                    }
                }


                if (svc.ExistsAsync("/単体テスト用/サブディレクトリ 1/テキスト1.txt").Result) return;
                for (int i=0; i<20; i++)
                {
                    var filename =  "/単体テスト用/サブディレクトリ 1/テキスト" + i.ToString() + ".txt";
                    if (svc.ExistsAsync(filename).Result)
                    {
                        var file = svc.GetFileAsync(CreateTestAdminUser(), filename).Result;
                        if (file.IsCheckOut) svc.CheckInAsync(CreateTestAdminUser(), filename).Wait();
                        svc.DeleteFileAsync(CreateTestAdminUser(), filename).Wait();
                    }

                    var content = "サンプルテキスト" + i.ToString();
                    if (i % 3 == 0)
                    {
                        content = content + " テスト";
                    }
                    using (var st = new MemoryStream(Encoding.UTF8.GetBytes(content)))
                    {
                        var file = svc.UploadFileAsync(CreateTestAdminUser(), filename, st).Result;
                        svc.UpdateFullTextAsync(CreateTestAdminUser(), filename, Path.GetFileName(filename), content).Wait();
                        if (i % 2 == 0)
                        {
                            file.SetAccessToken("everyone", "");
                            file.SetMetadataAsync().Wait();
                        }                   
                    }
                }
            }
        }
        //
        // クラス内のテストをすべて実行したら、ClassCleanup を使用してコードを実行してください
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // 各テストを実行する前に、TestInitialize を使用してコードを実行してください
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // 各テストを実行した後に、TestCleanup を使用してコードを実行してください
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        public static BlobUser CreateTestAdminUser()
        {
            return new BlobUser(new GenericPrincipal(new GenericIdentity("TestAdmin"), new string[] { })) { IsAdmin = true };
        }
        public static BlobUser CreateTestAdminUser2()
        {
            return new BlobUser(new GenericPrincipal(new GenericIdentity("TestAdmin2"), new string[] { })) { IsAdmin=true };
        }
        public static BlobUser CreateTestUser()
        {
            return new BlobUser(new GenericPrincipal(new GenericIdentity("Test"), new string[] { "users" }));
        }

        private static BlobService CreateService()
        {
            return new BlobService(new CloudAccount(ConfigurationManager.AppSettings["blobService.containerName"]), ConfigurationManager.AppSettings["blobService.connectionString"]);
        }

        [TestMethod]
        public void コンストラクタ()
        {
            using(var svc = CreateService())
            {

            }
        }

        [TestMethod]
        public void 一覧表示()
        {
            using (var svc = CreateService())
            {
                var list = svc.ListFilesAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 1/", 1000).Result;
                Assert.AreEqual(21, list.Count);
            }
        }

        [TestMethod]
        public void フルテキスト登録()
        {
            using (var svc = CreateService())
            {
                for (int i = 0; i < 20; i++)
                {
                    var filename = "/単体テスト用/サブディレクトリ 1/テキスト" + i.ToString() + ".txt";
                    var content = "サンプルテキスト" + i.ToString();
                    if (i % 3 == 0)
                    {
                      content = content + " テスト " + i.ToString();
                    }
                    svc.UpdateFullTextAsync(CreateTestAdminUser(), filename, Path.GetFileName(filename), content).Wait();
                }
            }
        }

        [TestMethod]
        public void フルテキスト登録_マルチスレッド()
        {
            System.Linq.Enumerable.Range(0, 20).AsParallel().ForAll((i) =>
            {
                var filename = "/単体テスト用/サブディレクトリ 1/テキスト" + i.ToString() + ".txt";
                var content = "サンプルテキスト" + i.ToString();
                if (i % 3 == 0)
                {
                    content = content + " テスト " + i.ToString();
                }
                using (var svc = CreateService())
                {
                    svc.UpdateFullTextAsync(CreateTestAdminUser(), filename, Path.GetFileName(filename), content).Wait();
                }
            });
        }


        [TestMethod]
        public void セキュリティトリミング表示()
        {
            using (var svc = CreateService())
            {
                var list = svc.ListFilesAsync(CreateTestUser(), "/単体テスト用/サブディレクトリ 1/", 1000).Result;
                Assert.AreEqual(11, list.Count);
            }
        }

        [TestMethod]
        public void 全文検索()
        {
            using (var svc = CreateService())
            {
                var list = svc.SearchAsync(CreateTestAdminUser(), "テスト", "/単体テスト用/サブディレクトリ 1/", 1000).Result;
                Assert.AreEqual(7, list.Count);
            }
        }

        [TestMethod]
        public void 全文検索セキュリティトリミング()
        {
            using (var svc = CreateService())
            {
                var list = svc.SearchAsync(CreateTestUser(), "テスト", "/単体テスト用/サブディレクトリ 1/", 1000).Result;
                Assert.AreEqual(3, list.Count);
            }
        }

        [TestMethod]
        public void タイトル全文検索()
        {
            using (var svc = CreateService())
            {
                var list = svc.SearchAsync(CreateTestAdminUser(), "キスト", "/単体テスト用/サブディレクトリ 1/", 1000).Result;
                Assert.AreEqual(20, list.Count);
            }
        }


        [TestMethod]
        public void 名前変更()
        {
            using (var svc = CreateService())
            {
                var filename = "/単体テスト用/サブディレクトリ 2/テキスト.txt";
                var filename2 = "/単体テスト用/サブディレクトリ 3/テキスト.txt";
                svc.RenewSearcherIndex();
                var fulltext = svc.GetFullTextAsync(CreateTestAdminUser(), filename).Result;
                Assert.IsFalse(string.IsNullOrEmpty(fulltext));
                svc.RenameFileAsync(CreateTestAdminUser(), filename, filename2).Wait();
                svc.EnqueuUpdateFullTextAsync(CreateTestAdminUser(), filename2, "テキスト.txt", null).Wait();
                svc.EnqueuDeleteFullTextAsync(CreateTestAdminUser(), filename).Wait();
                svc.ExecuteUpdateFullText().Wait();
            }
            using (var svc = CreateService())
            {
                var list = svc.ListFilesAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 2/", 1000).Result;
                Assert.AreEqual(3, list.Count);
                list = svc.SearchAsync(CreateTestAdminUser(), "キスト", "/単体テスト用/サブディレクトリ 2/", 1000).Result;
                Assert.AreEqual(1, list.Count);
                list = svc.ListFilesAsync(CreateTestAdminUser(), "/単体テスト用/サブディレクトリ 3/", 1000).Result;
                Assert.AreEqual(2, list.Count);
                list = svc.SearchAsync(CreateTestAdminUser(), "キスト", "/単体テスト用/サブディレクトリ 3/", 1000).Result;
                Assert.AreEqual(1, list.Count);

            }
        }



        [TestMethod]
        public void チェックイン_チェックアウト()
        {
            using (var svc = CreateService())
            {
                var filename = "/単体テスト用/サブディレクトリ 1/テキスト0.txt";

                if ( svc.IsCheckOutOtherAsync(CreateTestAdminUser(), filename).Result)
                {
                    var work1 = svc.CheckInAsync(CreateTestAdminUser(), filename).Result;
                }
                var file = svc.CheckOutAsync(CreateTestAdminUser(), filename).Result as BlobFile;
                Assert.AreEqual(true, file.IsCheckOut);

                try
                {
                    var work2 = svc.CheckOutAsync(CreateTestAdminUser2(), filename).Result;
                    Assert.Fail();
                }
                catch (AggregateException ex)
                {
                    var ext = ex.InnerException as BlobException;
                    Assert.AreEqual(BlobExceptionCode.Locked, ext.Code);
                }
                file = svc.CheckInAsync(CreateTestAdminUser(), filename).Result as BlobFile;
                Assert.AreEqual(false, file.IsCheckOut);
            }
        }

    }
}
